const { abs, sin, sign, cos, floor, round, acos, atan2, pow } = Math;
import { keep0s } from "./index.js";
/**
 * @param value
 * @return 度数转换为弧度
 */
const toRad = (value: number): number => (value * Math.PI) / 180;
/**
 * @param {number} value
 * @return {number} 弧度转换为度数
 */
const toDeg = (value: number): number => (value * 180) / Math.PI;
/**
 *
 * @param {number} value
 * @returns 将数字修正到[0,360)，可用于修正经度
 */
const toIn360 = (value: number): number => {
  while (value > 360) value -= 360;
  while (value <= 0) value += 360;
  return value;
};
/**
 * @param {number} value
 * @returns 将数字修正到(-180,180]，可用于修正纬度
 */
const toIn180 = (value: number) => {
  while (value >= 180) value -= 360;
  while (value <= -180) value += 360;
  return value;
};

export type CalDistRawReturnType = {
  /** 以米为单位的由点1指向点2的距离 */
  distance: number;
  /** 以度数为单位的由点1指向点2的，在点1位置测量，取值范围(-180,180] */
  initialBearing: number;
  /** 以度数为单位的由点1指向点2的，在点2位置测量，取值范围(-180,180]。 */
  finalBearing: number;
};
/**
 *
 * @param lat1 点1的纬度,单位是度数
 * @param lon1 点1的经度，单位是度数
 * @param lat2 点2的纬度,单位是度数
 * @param lon2 点2的经度,单位是度数
 * @param callback 传入函数以调用返回值
 * @returns 计算从(lat1.lon1)到(lat2,lon2)之间的参数, 以WGS-84坐标系精确计算,如果lat2或lat1相差超过180不能自动修正
 */
export function calDistRaw(
  lat1: number,
  lon1: number,
  lat2: number,
  lon2: number,
  callback: (res: CalDistRawReturnType) => void | any = () => 0
): CalDistRawReturnType {
  /* //修正lon2到与lon1在180°以内
      while(lon2-lon1>180){
        lon2-=360;
      }
      while(lon2-lon1<-180){
        lon2+=360;
      }
      */
  const a = 6378137;
  const b = 6356752.314245;
  const f = 1 / 298.257223563; // WGS-84 ellipsoid params
  const L = toRad(lon2 - lon1);
  const U1 = Math.atan((1 - f) * Math.tan(toRad(lat1)));
  const U2 = Math.atan((1 - f) * Math.tan(toRad(lat2)));
  const sinU1 = Math.sin(U1);
  const cosU1 = Math.cos(U1);
  const sinU2 = Math.sin(U2);
  const cosU2 = Math.cos(U2);

  let lambda = L;
  let lambdaP;
  let iterLimit = 100;
  let sinLambda;
  let cosLambda;
  let sinSigma;
  let result: CalDistRawReturnType;
  let cosSigma;
  let sigma;
  let sinAlpha;
  let cosSqAlpha;
  let cos2SigmaM;
  do {
    sinLambda = Math.sin(lambda);
    cosLambda = Math.cos(lambda);
    sinSigma = Math.sqrt(
      cosU2 * sinLambda * (cosU2 * sinLambda) +
        (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) *
          (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda)
    );
    if (sinSigma === 0) {
      result = {
        distance: 0,
        initialBearing: 0,
        finalBearing: 0,
      };
      if (callback !== undefined && callback instanceof Function) {
        if (callback.length === 3) {
          callback(result);
        } else {
          callback(result);
        }
      }
      return result;
    } // co-incident points
    cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
    sigma = Math.atan2(sinSigma, cosSigma);
    sinAlpha = (cosU1 * cosU2 * sinLambda) / sinSigma;
    cosSqAlpha = 1 - sinAlpha * sinAlpha;
    cos2SigmaM = cosSigma - (2 * sinU1 * sinU2) / cosSqAlpha;
    if (isNaN(cos2SigmaM)) cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6)
    const C = (f / 16) * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
    lambdaP = lambda;
    lambda =
      L +
      (1 - C) *
        f *
        sinAlpha *
        (sigma +
          C *
            sinSigma *
            (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
  } while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0);
  // @ts-ignore
  if (!iterLimit) return null; // formula failed to converge
  const uSq = (cosSqAlpha * (a * a - b * b)) / (b * b);
  const A = 1 + (uSq / 16384) * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
  const B = (uSq / 1024) * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
  const deltaSigma =
    B *
    sinSigma *
    (cos2SigmaM +
      (B / 4) *
        (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
          (B / 6) *
            cos2SigmaM *
            (-3 + 4 * sinSigma * sinSigma) *
            (-3 + 4 * cos2SigmaM * cos2SigmaM)));
  let s = b * A * (sigma - deltaSigma);

  s = Number(s.toFixed(3)); // round to 1mm precision

  // note: to return initial/final bearings in addition to distance, use something like:
  const fwdAz = Math.atan2(
    cosU2 * sinLambda,
    cosU1 * sinU2 - sinU1 * cosU2 * cosLambda
  );
  const revAz = Math.atan2(
    cosU1 * sinLambda,
    -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda
  );
  result = {
    distance: s,
    initialBearing: toDeg(fwdAz),
    finalBearing: toDeg(revAz),
  };

  if (callback !== undefined && callback instanceof Function) {
    if (callback.length === 3) {
      callback(result);
    } else {
      callback(result);
    }
  }

  return result;
}

interface CalPointRawReturnType {
  /** 计算后的维度 */
  lat: number;
  /** 计算后的经度 */
  lon: number;
  /** 在计算后的经纬度测量的bearing */
  finalBearing: number;
}

/**
 * @description 所有值的输入差一圈也可接受
 * @param {number} lat0 点1的纬度,单位是度数
 * @param {number} lon0 点1的经度，单位是度数
 * @param {Number} bearing: 以度数为单位的由点1指向点2的，在点1位置测量，取值范围(-180,180]。
 * @param {Number} dist: 以米为单位的由点1指向点2的距离。
 * @param {object} callback 返回值的对象
 * @returns {object} 已知(lat1.lon1)、一个方位角和一个距离，求(lat2,lon2),
 */

const calPointRaw = (
  lat0: number,
  lon0: number,
  bearing: number,
  dist: number,
  callback: (point: CalPointRawReturnType) => void | any = () => 0
): CalPointRawReturnType => {
  const a = 6378137;
  const b = 6356752.3142;
  const f = 1 / 298.257223563; /* WGS-84 ellipsoid */
  const s = dist;
  const alpha1 = toRad(bearing);
  const sinAlpha1 = Math.sin(alpha1);
  const cosAlpha1 = Math.cos(alpha1);

  const tanU1 = (1 - f) * Math.tan(toRad(lat0));
  const cosU1 = 1 / Math.sqrt(1 + tanU1 * tanU1);
  const sinU1 = tanU1 * cosU1;
  const sigma1 = Math.atan2(tanU1, cosAlpha1);
  const sinAlpha = cosU1 * sinAlpha1;
  const cosSqAlpha = 1 - sinAlpha * sinAlpha;
  const uSq = (cosSqAlpha * (a * a - b * b)) / (b * b);
  const A = 1 + (uSq / 16384) * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
  const B = (uSq / 1024) * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));

  let sigma = s / (b * A);
  let sigmaP = 2 * Math.PI;

  let cos2SigmaM: number = 0;
  let sinSigma: number = 0;
  let cosSigma: number = 0;
  while (Math.abs(sigma - sigmaP) > 1e-12) {
    cos2SigmaM = Math.cos(2 * sigma1 + sigma);
    sinSigma = Math.sin(sigma);
    cosSigma = Math.cos(sigma);
    const deltaSigma =
      B *
      sinSigma *
      (cos2SigmaM +
        (B / 4) *
          (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
            (B / 6) *
              cos2SigmaM *
              (-3 + 4 * sinSigma * sinSigma) *
              (-3 + 4 * cos2SigmaM * cos2SigmaM)));
    sigmaP = sigma;
    sigma = s / (b * A) + deltaSigma;
  }
  const tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
  const lat2 = Math.atan2(
    sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
    (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp)
  );
  const lambda = Math.atan2(
    sinSigma * sinAlpha1,
    cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1
  );
  const C = (f / 16) * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
  const L =
    lambda -
    (1 - C) *
      f *
      sinAlpha *
      (sigma +
        C *
          sinSigma *
          (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
  const lon2 = ((toRad(lon0) + L + 3 * Math.PI) % (2 * Math.PI)) - Math.PI; // normalise to -180...+180

  const revAz = Math.atan2(sinAlpha, -tmp); // final bearing, if required

  const result = {
    lat: toDeg(lat2),
    lon: toDeg(lon2),
    finalBearing: toDeg(revAz),
  };
  if (callback instanceof Function) {
    if (callback.length === 3) {
      callback(result);
    } else {
      callback(result);
    }
  }
  return result;
};

/**
 * @param lat1 点1的纬度,单位是度数
 * @param lon1 点1的经度，单位是度数
 * @param lat2 点2的纬度,单位是度数
 * @param lon2 点2的经度，单位是度数
 * @returns 计算点1至点2的方位，单位是度数，测量点位于点1
 */
export function geoInitialBearing(
  lat1: number,
  lon1: number,
  lat2: number,
  lon2: number
): number {
  // 修正lon2到与lon1在180°以内
  while (lon2 - lon1 > 180) lon2 -= 360;
  while (lon2 - lon1 < -180) lon2 += 360;
  return toIn360(calDistRaw(lat1, lon1, lat2, lon2).initialBearing);
}

/**
 *
 * @param lat1 点1的纬度,单位是度数
 * @param lon1 点1的经度，单位是度数
 * @param lat2 点2的纬度,单位是度数
 * @param lon2 点2的经度，单位是度数
 * @returns 计算点1至点2的方位，单位是度数，测量点位于点1
 */
export function geoCrs(
  lat1: number,
  lon1: number,
  lat2: number,
  lon2: number
): number {
  // 修正lon2到与lon1在180°以内
  while (lon2 - lon1 > 180) lon2 -= 360;
  while (lon2 - lon1 < -180) lon2 += 360;
  return toIn360(calDistRaw(lat1, lon1, lat2, lon2).initialBearing);
}

/**
 * @param lat1 点1的纬度,单位是度数
 * @param lon1 点1的经度，单位是度数
 * @param lat2 点2的纬度,单位是度数
 * @param lon2 点2的经度，单位是度数
 * @returns 计算点1至点2的方位，单位是度数，测量点位于点2
 */
export function geoFinalBearing(
  lat1: number,
  lon1: number,
  lat2: number,
  lon2: number
): number {
  // 修正lon2到与lon1在180°以内
  while (lon2 - lon1 > 180) lon2 -= 360;
  while (lon2 - lon1 < -180) lon2 += 360;
  return toIn360(calDistRaw(lat1, lon1, lat2, lon2).finalBearing);
}

/**
 * @param lat1 点1的纬度,单位是度数
 * @param lon1 点1的经度，单位是度数
 * @param lat2 点2的纬度,单位是度数
 * @param lon2 点2的经度，单位是度数
 * @returns 计算点1至点2的距离，单位是米
 */
export function geoDistM(
  lat1: number,
  lon1: number,
  lat2: number,
  lon2: number
): number {
  // 修正lon2到与lon1在180°以内
  while (lon2 - lon1 > 180) {
    lon2 -= 360;
  }
  while (lon2 - lon1 < -180) {
    lon2 += 360;
  }
  return calDistRaw(lat1, lon1, lat2, lon2).distance;
}

/**
 * @param lat1 点1的纬度,单位是度数
 * @param lon1 点1的经度，单位是度数
 * @param lat2 点2的纬度,单位是度数
 * @param lon2 点2的经度，单位是度数
 * @returns 计算点1至点2的距离，单位是海里
 */
export function geoDist(
  lat1: number,
  lon1: number,
  lat2: number,
  lon2: number
): number {
  // 修正lon2到与lon1在180°以内
  while (lon2 - lon1 > 180) lon2 -= 360;
  while (lon2 - lon1 < -180) lon2 += 360;
  return calDistRaw(lat1, lon1, lat2, lon2).distance / 1852;
}

/**
 * @param lat1 点1的纬度,单位是度数
 * @param lon1 点1的经度，单位是度数
 * @param brng 点1至点2的方位，单位是度数
 * @param dist 点1至点2的距离，单位是米
 * @returns 点2的纬度，单位是度数
 */
export function geoLatitudeM(
  lat1: number,
  lon1: number,
  brng: number,
  dist: number
): number {
  return calPointRaw(lat1, lon1, brng, dist).lat;
}

/**
 * @param lat1 点1的纬度,单位是度数
 * @param lon1 点1的经度，单位是度数
 * @param brng 点1至点2的方位，单位是度数
 * @param dist 点1至点2的距离，单位是米
 * @returns 点2的经度，单位是度数
 */
export function geoLongitudeM(
  lat1: number,
  lon1: number,
  brng: number,
  dist: number
): number {
  return calPointRaw(lat1, lon1, brng, dist).lon;
}

/**
 * @param lat1 点1的纬度,单位是度数
 * @param lon1 点1的经度，单位是度数
 * @param brng 点1至点2的方位，单位是度数
 * @param dist 点1至点2的距离，单位是海里
 * @returns 点2的纬度，单位是度数
 */
export function geoLatitude(
  lat1: number,
  lon1: number,
  brng: number,
  dist: number
): number {
  return calPointRaw(lat1, lon1, brng, dist * 1852).lat;
}

/**
 * @param lat1 点1的纬度,单位是度数
 * @param lon1 点1的经度，单位是度数
 * @param brng 点1至点2的方位，单位是度数
 * @param dist 点1至点2的距离，单位是海里
 * @returns 点2的经度，单位是度数
 */
export function geoLongitude(
  lat1: number,
  lon1: number,
  brng: number,
  dist: number
): number {
  return calPointRaw(lat1, lon1, brng, dist * 1852).lon;
}

/**
 * @param str 经或纬度字符串，不能同时输入两个
 * @param decimals 经纬度的小数保留位数，默认为10
 * @returns 转换后的经纬度
 */
export function geoSmart(str: string | number, decimals = 10): number {
  str = (str + "").toUpperCase();
  const isNeg =
    str.indexOf("S") !== -1 ||
    str.indexOf("W") !== -1 ||
    str.indexOf("-") !== -1;
  const hLen = str.indexOf("W") !== -1 || str.indexOf("E") !== -1 ? 3 : 2; // 如果是经度，那么经度的度数为3，其他都是2
  // 方案1-----------
  const pre = {
    hh: "",
    mm: "",
    ss: "",
  };
  for (const _temp of str) {
    if (/^[0-9]*$/.test(_temp) || _temp === ".") {
      if (
        pre.hh.length < hLen ||
        pre.hh.indexOf(".") !== -1 ||
        (pre.mm === "" && _temp === ".")
      ) {
        pre.hh += _temp;
      } else if (
        pre.mm.length < 2 ||
        pre.mm.indexOf(".") !== -1 ||
        (pre.ss === "" && _temp === ".")
      ) {
        pre.mm += _temp;
      } else {
        if (pre.ss.length === 2 && _temp !== ".") pre.ss += ".";
        pre.ss += _temp;
      }
    } else if ((pre.hh + pre.mm + pre.ss).length > 5) {
      break;
    }
  }
  if (pre.ss.length === 1) {
    pre.mm += "." + pre.ss;
    pre.ss = "";
  }
  if (isNeg) {
    str = -pre.hh - +pre.mm / 60 - +pre.ss / 3600;
  } else {
    str = pre.hh + +pre.mm / 60 + +pre.ss / 3600;
  }
  str = round(+str * pow(10, decimals)) / pow(10, decimals);
  return str;
}

/**
 * (所有速度单位只要一致即可)
 * @param trk 航迹，以°为单位
 * @param tas 真空速
 * @param wd 风向，以°为单位
 * @param ws 风速
 * @returns 地速
 */
export function e6bGS(
  trk: number,
  tas: number,
  wd: number,
  ws: number
): number {
  const angleA = toIn180(trk + 180 - wd); // 航迹与风去向夹角
  return (
    pow(pow(ws * cos(angleA / 57.2957795), 2) + pow(tas, 2) - pow(ws, 2), 0.5) +
    ws * cos(angleA / 57.2957795)
  );
}

/**
 * (所有速度单位只要一致即可)
 * @param hdg 航向，以°为单位
 * @param tas 真空速
 * @param wd 风向，以°为单位
 * @param ws 风速
 * @returns 地速
 */
export function e6bGSbyHDG(
  hdg: number,
  tas: number,
  wd: number,
  ws: number
): number {
  return pow(
    pow(
      cos((90 - hdg) / 57.2957795) * tas - cos((90 - wd) / 57.2957795) * ws,
      2
    ) +
      pow(
        sin((90 - hdg) / 57.2957795) * tas - sin((90 - wd) / 57.2957795) * ws,
        2
      ),
    0.5
  );
}

/**
 * (所有速度单位只要一致即可)
 * @param trk 航迹，以°为单位
 * @param tas 真空速
 * @param wd 风向，以°为单位
 * @param ws 风速
 * @returns 偏流角，以°为单位，右侧风时值为负数
 */
export function e6bDA(
  trk: number,
  tas: number,
  wd: number,
  ws: number
): number {
  const angleA = toIn180(trk + 180 - wd);
  const gs =
    pow(pow(ws * cos(angleA / 57.2957795), 2) + pow(tas, 2) - pow(ws, 2), 0.5) +
    ws * cos(angleA / 57.2957795);
  const da =
    -acos((pow(tas, 2) + pow(gs, 2) - pow(ws, 2)) / (2 * tas * gs)) *
    57.2957795 *
    sign(sin(angleA / 57.2957795));
  return ws === 0 ? 0 : toIn180(da);
}

/**
 * (所有速度单位只要一致即可)
 * @param trk 航迹，以°为单位
 * @param tas 真空速
 * @param wd 风向，以°为单位
 * @param ws 风速
 * @returns 航向，以°为单位
 */
export function e6bHDG(trk: number, tas: number, wd: number, ws: number) {
  const angleA = toIn180(trk + 180 - wd);
  const gs =
    pow(pow(ws * cos(angleA / 57.2957795), 2) + pow(tas, 2) - pow(ws, 2), 0.5) +
    ws * cos(angleA / 57.2957795);
  const da =
    -acos((pow(tas, 2) + pow(gs, 2) - pow(ws, 2)) / (2 * tas * gs)) *
    57.2957795 *
    sign(sin(angleA / 57.2957795));
  return toIn360(trk - (ws === 0 ? 0 : toIn180(da)));
}

/**
 * (所有速度单位只要一致即可)
 * @param trk 航迹，以°为单位
 * @param gs 地速
 * @param wd 风向，以°为单位
 * @param ws 风速
 * @returns 航向，以°为单位
 */
export function e6bHDGbyGS(
  trk: number,
  gs: number,
  wd: number,
  ws: number
): number {
  if (ws === 0) {
    return toIn360(trk);
  }
  return toIn360(
    90 -
      atan2(
        sin((90 - trk) / 57.2957795) * gs + sin((90 - wd) / 57.2957795) * ws,
        cos((90 - trk) / 57.2957795) * gs + cos((90 - wd) / 57.2957795) * ws
      ) *
        57.2957795 <
      0
      ? 90 -
          atan2(
            sin((90 - trk) / 57.2957795) * gs +
              sin((90 - wd) / 57.2957795) * ws,
            cos((90 - trk) / 57.2957795) * gs + cos((90 - wd) / 57.2957795) * ws
          ) *
            57.2957795 +
          360
      : 90 -
          atan2(
            sin((90 - trk) / 57.2957795) * gs +
              sin((90 - wd) / 57.2957795) * ws,
            cos((90 - trk) / 57.2957795) * gs + cos((90 - wd) / 57.2957795) * ws
          ) *
            57.2957795
  );
}

/**
 * (所有速度单位只要一致即可)
 * @param trk 航迹，以°为单位
 * @param gs 地速
 * @param wd 风向，以°为单位
 * @param ws 风速
 * @returns 真空速
 */
export function e6bTAS(
  trk: number,
  gs: number,
  wd: number,
  ws: number
): number {
  return pow(
    pow(
      cos((90 - trk) / 57.2957795) * gs + cos((90 - wd) / 57.2957795) * ws,
      2
    ) +
      pow(
        sin((90 - trk) / 57.2957795) * gs + sin((90 - wd) / 57.2957795) * ws,
        2
      ),
    0.5
  );
}

/**
 * (所有速度单位只要一致即可)
 * @param trk 航迹，以°为单位
 * @param gs 地速
 * @param hdg 航向，以°为单位
 * @param tas 真空速
 * @returns 风速
 */
export function e6bWS(
  trk: number,
  gs: number,
  hdg: number,
  tas: number
): number {
  return pow(
    pow(
      cos((90 - hdg) / 57.2957795) * tas - cos((90 - trk) / 57.2957795) * gs,
      2
    ) +
      pow(
        sin((90 - hdg) / 57.2957795) * tas - sin((90 - trk) / 57.2957795) * gs,
        2
      ),
    0.5
  );
}

/**
 * (所有速度单位只要一致即可)
 * @param trk 航迹，以°为单位
 * @param gs 地速
 * @param hdg 航向，以°为单位
 * @param tas 真空速
 * @returns 风向，以°为单位
 */
export function e6bWD(
  trk: number,
  gs: number,
  hdg: number,
  tas: number
): number {
  return toIn360(
    90 -
      atan2(
        sin((90 - hdg) / 57.2957795) * tas - sin((90 - trk) / 57.2957795) * gs,
        cos((90 - hdg) / 57.2957795) * tas - cos((90 - trk) / 57.2957795) * gs
      ) *
        57.2957795 <
      0
      ? 90 -
          atan2(
            sin((90 - hdg) / 57.2957795) * tas -
              sin((90 - trk) / 57.2957795) * gs,
            cos((90 - hdg) / 57.2957795) * tas -
              cos((90 - trk) / 57.2957795) * gs
          ) *
            57.2957795 +
          360
      : 90 -
          atan2(
            sin((90 - hdg) / 57.2957795) * tas -
              sin((90 - trk) / 57.2957795) * gs,
            cos((90 - hdg) / 57.2957795) * tas -
              cos((90 - trk) / 57.2957795) * gs
          ) *
            57.2957795
  );
}

/**
 * (所有速度单位只要一致即可)
 * @param hdg 航向，以°为单位
 * @param tas 真空速
 * @param wd 风向，以°为单位
 * @param ws 风速
 * @returns 航迹，以°为单位
 */
export function e6bTRK(
  hdg: number,
  tas: number,
  wd: number,
  ws: number
): number {
  return toIn360(
    90 -
      atan2(
        sin((90 - hdg) / 57.2957795) * tas - sin((90 - wd) / 57.2957795) * ws,
        cos((90 - hdg) / 57.2957795) * tas - cos((90 - wd) / 57.2957795) * ws
      ) *
        57.2957795 <
      0
      ? 90 -
          atan2(
            sin((90 - hdg) / 57.2957795) * tas -
              sin((90 - wd) / 57.2957795) * ws,
            cos((90 - hdg) / 57.2957795) * tas -
              cos((90 - wd) / 57.2957795) * ws
          ) *
            57.2957795 +
          360
      : 90 -
          atan2(
            sin((90 - hdg) / 57.2957795) * tas -
              sin((90 - wd) / 57.2957795) * ws,
            cos((90 - hdg) / 57.2957795) * tas -
              cos((90 - wd) / 57.2957795) * ws
          ) *
            57.2957795
  );
}

/**
 * @param args 度数
 * @returns 将所有度数加到一起之后，取值至(-180,180]
 */
export function in180s(...args: (number | string)[]): number {
  let _val = 0;
  for (let val of args) _val += +val;
  return toIn180(_val);
}

/**
 * @param args 度数
 * @returns 将所有度数加到一起之后，取值至[0,360)
 */
export function in360s(...args: (number | string)[]): number {
  let _val = 0;
  for (let val of args) _val += +val;
  return toIn360(_val);
}

/**
 * @param mode 模式: 默认为0; 其绝对值为保留小数点后几位; 如果是大于等于0，则使用 :分隔，否则使用 °'" 分隔
 * @param args 秒数
 * @returns 计算出经纬度或者时间，以字符串形式返回 "度分秒"
 */
export function sToHms(mode = 0, ...args: number[]): string {
  let _temp = 0; // 总秒数
  const _rounder = abs(round(mode)); // 保留几个小数点
  for (let v of args) _temp += +v;
  const _sgn = _temp < 0 ? "-" : ""; // 是否为-
  _temp = abs(_temp);
  const hh = floor(_temp / 3600);
  const mm = floor((_temp - hh * 3600) / 60);
  let ss = _temp - hh * 3600 - mm * 60;
  ss = round(ss * pow(10, _rounder)) / pow(10, _rounder);
  if (mode < 0) {
    return _sgn + `${keep0s(hh, 2)}:${keep0s(mm, 2)}:${keep0s(ss, 2)}`;
  } else {
    return _sgn + `${keep0s(hh, 2)}° ${keep0s(mm, 2)}' ${keep0s(ss, 2)}"`;
  }
}

/**
 * @param mode 模式: 默认为0; 其绝对值为保留小数点后几位; 如果是大于等于0，则使用 :分隔，否则使用 °'" 分隔
 * @param args 秒数
 * @returns 计算出经纬度或者时间，以字符串形式返回 "度分"
 */
export function sToHm(mode = 0, ...args: number[]) {
  let _temp = 0; // 总秒数
  const _rounder = abs(round(mode)); // 保留几个小数点
  for (let v of args) _temp += +v;
  const _sgn = _temp < 0 ? "-" : ""; // 是否为-
  _temp = abs(_temp);
  const hh = floor(_temp / 3600);
  let mm = (_temp - hh * 3600) / 60;
  mm = round(mm * pow(10, _rounder)) / pow(10, _rounder);
  if (mode < 0) {
    return _sgn + `${keep0s(hh, 2)}:${keep0s(mm, 2)}`;
  } else {
    return _sgn + `${keep0s(hh, 2)}° ${keep0s(mm, 2)}"`;
  }
}

/**
 * @param mode 模式: 默认为0; 其绝对值为保留小数点后几位; 如果是大于等于0，则使用 :分隔，否则使用 °'" 分隔
 * @param args 秒数
 * @returns 计算出经纬度或者时间，以字符串形式返回 "分秒"
 */
export function sToMs(mode = 0, ...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    const _rounder = abs(round(mode)); // 保留几个小数点
    for (let v of args) _temp += +v;
    const _sgn = _temp < 0 ? "-" : ""; // 是否为-
    _temp = abs(_temp);
    const mm = floor(_temp / 60);
    let ss = _temp - mm * 60;
    ss = round(ss * pow(10, _rounder)) / pow(10, _rounder);
    if (mode < 0) {
      return _sgn + `${keep0s(mm, 2)}:${keep0s(ss, 2)}`;
    } else {
      return _sgn + `${keep0s(mm, 2)}' ${keep0s(ss, 2)}"`;
    }
  } catch (e) {
    console.warn(e);
  }
}

/**
 * @param mode 模式: 默认为0; 其绝对值为保留小数点后几位; 如果是大于等于0，则使用 :分隔，否则使用 °'" 分隔
 * @param args 分钟数
 * @returns 计算出经纬度或者时间，以字符串形式返回 "度分秒"
 */
export function mToHms(mode = 0, ...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    for (let v of args) _temp += +v * 60;
    return sToHms(mode, _temp);
  } catch (e) {
    console.warn(e);
  }
}

/**
 * @param mode 模式: 默认为0; 其绝对值为保留小数点后几位; 如果是大于等于0，则使用 :分隔，否则使用 °'" 分隔
 * @param args 分钟数
 * @returns 计算出经纬度或者时间，以字符串形式返回 "度分"
 */
export function mToHm(mode = 0, ...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    for (let v of args) _temp += +v * 60;
    for (const i in arguments) {
      if (+i === 0) continue;
      _temp += +arguments[i] * 60;
    }
    return sToHm(mode, _temp);
  } catch (e) {
    console.warn(e);
  }
}

/**
 * @param mode 模式: 默认为0; 其绝对值为保留小数点后几位; 如果是大于等于0，则使用 :分隔，否则使用 °'" 分隔
 * @param args 分钟数
 * @returns 计算出经纬度或者时间，以字符串形式返回 "分秒"
 */
export function mToMs(mode = 0, ...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    for (let v of args) _temp += +v * 60;
    return sToMs(mode, _temp);
  } catch (e) {
    console.warn(e);
  }
}

/**
 * @param mode 模式: 默认为0; 其绝对值为保留小数点后几位; 如果是大于等于0，则使用 :分隔，否则使用 °'" 分隔
 * @param args 小时数 / 度数
 * @returns 计算出经纬度或者时间，以字符串形式返回 "度分秒"
 */
export function hToHms(mode = 0, ...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    for (let v of args) _temp += +v * 3600;
    return sToHms(mode, _temp);
  } catch (e) {
    console.warn(e);
  }
}

/**
 * @param mode 模式: 默认为0; 其绝对值为保留小数点后几位; 如果是大于等于0，则使用 :分隔，否则使用 °'" 分隔
 * @param args 小时数 / 度数
 * @returns 计算出经纬度或者时间，以字符串形式返回 "度分"
 */
export function hToHm(mode = 0, ...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    for (let v of args) _temp += +v * 3600;
    return sToHm(mode, _temp);
  } catch (e) {
    console.warn(e);
  }
}

/**
 * @param args 小时数 / 度数
 * @returns 计算出经纬度或者时间，以字符串形式返回 "分秒"，小数点后保留1位
 */
export function hToMs(...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    for (let v of args) _temp += +v * 3600;
    return sToMs(-1, _temp);
  } catch (e) {
    console.warn(e);
  }
}

/**
 * @param args 纬度
 * @returns N/S 度°分'，小数点后保留1位
 */
export function latToHm(...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    for (let v of args) _temp += +v * 3600;
    const res0 = _temp > 0 ? "N" : "S";
    const res1 = sToHm(1, _temp).replace(/-/g, "");
    return res0 + res1;
  } catch (e) {
    console.warn(e);
  }
}

/**
 * @param args 纬度
 * @returns N/S 度°分'秒"，小数点后保留1位
 */
export function latToHms(...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    for (let v of args) _temp += +v * 3600;
    const res0 = _temp > 0 ? "N" : "S";
    const res1 = sToHm(1, _temp).replace(/-/g, "");
    return res0 + res1;
  } catch (e) {
    console.warn(e);
  }
}

/**
 * @param args 经度
 * @returns W/E 度°分'，小数点后保留1位
 */
export function lonToHm(...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    for (let v of args) _temp += +v * 3600;
    const res0 = _temp > 0 ? "E" : "W";
    let res1 = sToHm(1, _temp).replace(/-/g, "");
    if (/^\d{2}°/.test(res1)) {
      res1 = "0" + res1;
    }
    return res0 + res1;
  } catch (e) {
    console.warn(e);
  }
}

/**
 * @param args 经度
 * @returns W/E 度°分'秒"，小数点后保留1位
 */
export function lonToHms(...args: number[]) {
  try {
    let _temp = 0; // 总秒数
    for (let v of args) _temp += +v * 3600;
    const res0 = _temp > 0 ? "E" : "W";
    let res1 = sToHms(1, _temp).replace(/-/g, "");
    if (/^\d{2}°/.test(res1)) {
      res1 = "0" + res1;
    }
    return res0 + res1;
  } catch (e) {
    console.warn(e);
  }
}

/**
 * 可以给普通用户使用的经纬度、E6B计算简易化函数
 */
export const geoHuman = {
  padding: keep0s,
  geoInitialBearing,
  geoCrs,
  geoFinalBearing,
  geoDistM,
  geoDist,
  geoLatitudeM,
  geoLongitudeM,
  geoLatitude,
  geoLongitude,
  geoSmart,
  e6bGS,
  e6bGSbyHDG,
  e6bDA,
  e6bHDG,
  e6bHDGbyGS,
  e6bTAS,
  e6bWS,
  e6bWD,
  e6bTRK,
  in180s,
  in360s,
  sToHms,
  sToHm,
  sToMs,
  mToHms,
  mToHm,
  mToMs,
  hToHms,
  hToHm,
  hToMs,
  latToHm,
  latToHms,
  lonToHm,
  lonToHms,
};

export default geoHuman;
